<pre><?php
/*

    ZendoFramework Arbitrary Precision Calculator
        < http://zendoframework.org/zendo.php.math >

    Copyright 2008 - The ZendoFramework Development Team
        < http://zendoframework.org/team >

    ZendoFramework is released under the
    GNU General Public License (GPL) version 3
    provided with this release in license.txt
    or via web at < http://gnu.org/licenses/gpl.txt >

*/

function zendo_apc_scale($newScale = false, $returnNewIfModified = false)
{
	static $scale = 30;
    
    if($newScale === false) return $scale;
    
    $exScale = $scale;
    $scale = $newScale;    
    return $returnNewIfModified ? $scale : $exScale;
}

class Zendo_Apc_Number
{
	var $intValue = '';
	var $decValue = '';
    
    var $negative = false;
    var $infinite = false;
    
    var $NaN = false;
    
    var $isApcNumber = true;
    
    function init($number)
    {
    	return $this->Zendo_Apc_Number($number);
    }
    
    function Zendo_Apc_Number($number = false)
    {
        if($number === false)
            return false;

    	$number = trim((string) $number);
        
    	if(strtolower($number) == 'nan')
        {
        	$this->NaN = true;
            return;
        }
        
        if(preg_match('/(\+|-|)(infinity|infinite)/i', $number, $pregged))
        {
            $this->infinite = true;
            if($pregged[1] == '-') $this->negative = true;
            return;
        }
        
        // Clean up number!
    	$number = preg_replace('/[^\d\.+\-e]/i', '', $number);
    	if(preg_match('/^(\+|-|)(\d*|)(?:\.(\d*|))?(?:e(\+|-|)(\d+))?$/i', $number, $pregged))
        {
            switch(count($pregged))
            {
            	case 3:
                    $this->intValue = ($pregged[2] == '') ? '0' : $pregged[2];
                    if($pregged[1] == '-') $this->negative = true;
                    return;
                case 4:
                    $this->intValue = ($pregged[2] == '') ? '0' : $pregged[2];
                    $this->decValue = ($pregged[3] == '') ? '0' : $pregged[3];
                    if($pregged[1] == '-') $this->negative = true;
                    return;
                case 6:
                    $this->intValue = ($pregged[2] == '') ? '0' : $pregged[2];
                    $this->decValue = ($pregged[3] == '') ? '0' : $pregged[3];
                    if($pregged[1] == '-') $this->negative = true;
                    
                    $exp = (int) $pregged[5];
                    
                    if($pregged[4] == '-')
                    {
                    	for($i=0; $i<$exp; $i++)
                        {
                        	if(ltrim($this->intValue, '0') == '')
                            {
                            	$this->decValue = '0'.$this->decValue;
                                continue;
                            }
                            
                            $this->decValue = substr($this->intValue, -1, 1).$this->decValue;
                            $this->intValue = substr($this->intValue, 0, -1);
                        }
                    }
                    else
                    {
                    	for($i=0; $i<$exp; $i++)
                        {
                            if(rtrim($this->decValue, '0') == '')
                            {
                                $this->intValue .= '0';
                                continue;
                            }
                            
                            $this->intValue .= substr($this->decValue, 0, 1);
                            $this->decValue = substr($this->decValue.'0', 1, -1);
                        }
                    }
                    
                    return;
            }
        }
        else
        {
        	$this->NaN = true;
            return;
        }
    }
    
    function serialize()
    {
    	if($this->NaN) return 'NaN';
    	if($this->infinite) return ($this->negative ? '-' : '').'Infinity';
        
    	return ($this->negative ? '-' : '').
               ((($int = ltrim($this->intValue, '0')) == '') ? '0' : $int).
               '.'.
               ((($dec = rtrim($this->decValue, '0')) == '') ? '0' : $dec);
    }

    function scale($scale = false)
    {
    	if(!$scale)
    	   $scale = zendo_apc_scale();
        
        $this->intValue = ltrim($this->intValue, '0');
        $this->decValue = rtrim($this->decValue, '0');
        
        if(strlen($this->decValue) <= $scale)
            return true;
            
        $offset = $scale;
        while(true)
        {
        	if($offset >= 0)
            {
            	if($this->decValue[$offset] < 5)
                	break;
                
                $offset--;
            }
            else
            {
            	if($this->intValue[strlen($this->intValue)+$offset] < 5)
                    break;
                
                $offset--;
            }
            
            while(true)
            {
                if($offset >= 0)
                {
                    if(($this->decValue[$offset]+1) > 9)
                    {
                    	$this->decValue[$offset] = 0;
                        $offset--;
                    }
                    else
                    {
                    	$this->decValue[$offset]++;
                        break;
                    }
                }
                else
                {
                	if(($diff = (strlen($this->intValue)+$offset)) < 0)
                        $this->intValue = str_repeat('0', $diff*-1).$this->intValue;
                            
                	if(($this->intValue[strlen($this->intValue)+$offset]+1) > 9)
                    {
                        $this->intValue[strlen($this->intValue)+$offset] = 0;
                        $offset--;
                    }
                    else
                    {               
                        $this->intValue[strlen($this->intValue)+$offset] = $this->intValue[strlen($this->intValue)+$offset]+1;
                        break;
                    }
                }
            }
        }
        
        if($scale == 0)
            $this->decValue = '';
        else
            $this->decValue = substr($this->decValue, 0, $scale);
    }
    
    function add($number)
    {
    	if(!isset($number->isApcNumber))
            $number = new Zendo_Apc_Number($number);
            
        $this->scale();
        $add->scale();
            
        if($this->NaN || $number->NaN)
        {
        	$result = new Zendo_Apc_Number('NaN');
            return $result;
        }
            
        if($this->infinite || $number->infinite)
        {
        	if($this->negative == $number->negative)
            {
            	$result = new Zendo_Apc_Number;
            	$result->infinite = true;
                $result->negative = $this->negative;
                return $result;
            }
            elseif($this->infinite && $number->infinite)
            {
            	// If the two operands are infinite 
                // (and not both negative or potitive), the result 
                // is always null.
            	$result = new Zendo_Apc_Number;
                return $result;
            }
            else
            {
            	$result = new Zendo_Apc_Number;
                $result->infinite = true;

            	if($this->infinite)
                    $result->negative = $this->negative;
                else
                    $result->negative = $number->negative;
                
                return $result;
            }
        }
        
        if(strlen($this->decValue) > strlen($number->decValue))
        	$number->decValue .= str_repeat('0', strlen($this->decValue)-strlen($number->decValue));
        elseif(strlen($this->decValue) < strlen($number->decValue))
            $this->decValue .= str_repeat('0', strlen($number->decValue)-strlen($this->decValue));
            
        $result = new Zendo_Apc_Number;
        $result->decValue = str_repeat('0', strlen($this->decValue));
        
        $buffer = 0;
        for($i=(strlen($this->decValue)-1); $i>=0; $i--)
        {
        	$tmp = $this->decValue[$i]+$number->decValue[$i]+$buffer;
            $buffer = 0;
            
            if($tmp > 9)
            {
            	$tmp -= 10;
                $buffer = 1;
            }
            
        	$result->decValue[$i] = $tmp;
        }
        
        for($i=(strlen($this->intValue)-1); $i>=0; $i--)
        {
            $tmp = $this->intValue[$i]+$number->intValue[$i]+$buffer;
            $buffer = 0;
                
            if($tmp > 9)
            {
                $tmp -= 10;
                $buffer = 1;
            }
                
            $result->intValue = $tmp.$result->intValue;
        }
        
        return $result;
    }
    
    function substrac($number)
    {
    	if(!isset($number->isApcNumber))
            $number = new Zendo_Apc_Number($number);
            
        $this->scale();
        $number->scale();
            
        if($this->NaN || $number->NaN)
        {
            $result = new Zendo_Apc_Number('NaN');
            return $result;
        }
            
        if($this->infinite || $number->infinite)
        {
            if($this->negative == $number->negative)
            {
                $result = new Zendo_Apc_Number;
                $result->infinite = true;
                $result->negative = $this->negative;
                return $result;
            }
            elseif($this->infinite && $number->infinite)
            {
                // If the two operands are infinite 
                // (and not both negative or potitive), the result 
                // is always null.
                $result = new Zendo_Apc_Number;
                return $result;
            }
            else
            {
                $result = new Zendo_Apc_Number;
                $result->infinite = true;

                if($this->infinite)
                    $result->negative = $this->negative;
                else
                    $result->negative = $number->negative;
                
                return $result;
            }
        }
    }
}

// This class will be deleted in a near future
class Zendo_Apc
{
	function scale($newScale = false)
    {
        zendo_apc_scale($newScale);
    }
}

//
//
// Tests below!
error_reporting(E_ALL);

$nb = new Zendo_Apc_Number('10');

$addRes = $nb->add('-10');

echo $addRes->serialize()."<br>";